package edu.yang.dbutils;

import java.sql.*;

/**
 * SQL执行器，核心入口类
 * @author yang
 * @date 2024-05-11
 */
public class SqlExecutor {
    /**
     * 连接对象
     */
    private final Connection connection;

    /**
     * 构造方法传入一个Connection对象
     * @param connection
     */
    public SqlExecutor(Connection connection) {
        this.connection = connection;
    }

    /**
     * 通用的增删改操作方法
     * @param sql
     * @param params
     * @return
     */
    public int executeUpdate(String sql, Object... params){
        //判断 连接对象 和 sql执行语句 是否为空
        if (connection == null){
            throw new RuntimeException("Null connection.");
        }
        if (sql == null || sql.isEmpty()){
            throw new RuntimeException("Null sql statement.");
        }
        //声明SQL预编译处理器对象
        PreparedStatement ps = null;
        try {
            //预编译发送sql并返回ps对象
            ps = connection.prepareStatement(sql);
            //设置参数
            setParameters(ps, params);
            //执行sql返回受影响的行数
            return ps.executeUpdate();
        } catch (SQLException e) {
            throw new RuntimeException("execute falil.", e);
        }finally {
            close(ps);
            close();
        }
    }

    /**
     * 增删改批量操作
     * @param sql
     * @param params
     * @return
     */
    public int[] executeBatch(String sql, Object[][] params){
        if (connection == null) {
            throw new RuntimeException("Null connection.");
        }
        if(sql == null || sql.isEmpty()){
            throw new RuntimeException("Null sql statement.");
        }
        //声明SQL预编译处理器
        PreparedStatement ps = null;
        try {
            //预编译发送sql并返回ps对象
            ps = connection.prepareStatement(sql);
            //设置参数
            for (Object[] param : params) {
                setParameters(ps, param);
                //将PS的操作缓存到内存中，最后再批量提交到数据库中
                ps.addBatch();
            }
            //批量操作
            return ps.executeBatch();
        } catch (SQLException e) {
            throw new RuntimeException("execute falil.", e);
        }finally {
            close(ps);
            close();
        }
    }

    public <T> T executeQuery(String sql, ResultSetHandler<T> handler, Object... params){
        if (connection == null) {
            throw new RuntimeException("Null connection.");
        }
        if (sql == null || sql.isEmpty()) {
            throw new RuntimeException("Null sql statement.");
        }
        if (handler == null) {
            throw new RuntimeException("Null handler.");
        }
        //声明SQL预编译处理器
        PreparedStatement ps = null;
        //声明结果集处理器
        ResultSet rs = null;
        //声明返回结果对象
        T result = null;
        try {
            //预编译SQL返回ps对象
            ps = connection.prepareStatement(sql);
            //设置参数
            setParameters(ps, params);
            //执行并返回结果集对象
            rs = ps.executeQuery();
            //将结果集交给结果集处理器去处理并返回处理结果
            result = handler.handle(rs);
            return result;

        } catch (SQLException e) {
            throw new RuntimeException("execute falil.", e);
        }finally {
            close(rs);
            close(ps);
            close();
        }
    }

    /**
     * 设置sql参数
     * @param ps
     * @param params
     * 通过传入的 SQL语句编译器对象 ps 和 不定长参数 params，通过 ps.setObject(int parameterIndex（从1开始）, Object x)
     * 方法将给定的 Object 参数设置为 SQL语句 中的指定参数（填入 sql 语句中对应的占位符 (?) ）
     */
    private void setParameters(PreparedStatement ps, Object... params)throws SQLException {
        for (int i = 0; i < params.length; i++){
            ps.setObject(i + 1, params[i]);
        }
    }

    /**
     * 关闭预编译器资源
     * @param st
     */
    private void close(Statement st){
        if (st != null){
            try {
                st.close();
            } catch (SQLException e) {
                throw new RuntimeException("Close statement fail.", e);
            }
        }
    }

    /**
     * 关闭结果集处理器
     * @param rs
     */
    private void close(ResultSet rs){
        if (rs!= null){
            try {
                rs.close();
            } catch (SQLException e) {
                throw new RuntimeException("Close result fail.", e);
            }
        }
    }

    /**
     * 关闭连接对象资源
     */
    private void close(){
        if (connection != null){
            try {
                connection.close();
            } catch (SQLException e) {
                throw new RuntimeException("Close connection fail.", e);
            }
        }
    }
}
